﻿using System;
using SpiceNet.Circuits;

namespace SpiceNet.Parameters
{
    /// <summary>
    /// A parameter that has at least a Value and a Given-flag.
    /// Optional are an input and output function where a circuit can be passed to.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class Parameter<T>
    {
        /// <summary>
        /// Gets or sets the value of the parameter
        /// </summary>
        public T Value { get; set; }

        /// <summary>
        /// Gets whether or not the parameter is given
        /// </summary>
        public bool Given { get; private set; } = false;

        /// <summary>
        /// Create a parameter
        /// </summary>
        /// <param name="defvalue"></param>
        public Parameter(T defvalue = default(T))
        {
            Value = defvalue;
        }

        /// <summary>
        /// Clone the parameter
        /// </summary>
        /// <returns></returns>
        public Parameter<T> Clone()
        {
            Parameter<T> clone = new Parameter<T>()
            {
                Given = this.Given,
                Value = this.Value
            };
            return clone;
        }

        /// <summary>
        /// Set all parameters to this one
        /// </summary>
        /// <param name="source"></param>
        public void Set(Parameter<T> source)
        {
            Given = source.Given;
            Value = source.Value;
        }

        /// <summary>
        /// Set the parameter and flags the parameter as Given
        /// </summary>
        /// <param name="value"></param>
        /// <param name="ckt"></param>
        public void Par(T value, Circuit ckt = null)
        {
            Value = value;
            Given = true;
        }

        /// <summary>
        /// Implicit cast to type
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        public static implicit operator T(Parameter<T> p)
        {
            return p.Value;
        }

        /// <summary>
        /// Convert to string
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            object v = (object)this.Value;
            if (v is Double)
                return ((double)v).ToString("e5");
            if (v is bool)
                return ((bool)v) ? "True" : "False";
            return v?.ToString() ?? "NULL";
        }
    }
}
